const fs = require('fs-extra')
const path = require('path')
const uuid = require('uuid/v5')
const marked = require('marked')
const algoliasearch = require('algoliasearch')
const argv = require('yargs').argv
// const minify = require('html-minifier').minify

let isBuildSearchIndexs = argv['search']

const enterDir = path.resolve('D:/_work/note-md/docs-other/note')
// const enterDir = path.resolve('E:/note2')
// const outputDir = path.resolve('E:/note-output')
const outputDir = path.resolve('D:/_work/cqlql.github.io/note/data')

/* 代码高亮 */
// 所有语言
// const highlight = require('highlight.js')
// 指定语言
const hljs = require('highlight.js/lib/highlight.js')
hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml'))
hljs.registerLanguage('css', require('highlight.js/lib/languages/css'))
hljs.registerLanguage('javascript', require('highlight.js/lib/languages/javascript'))
hljs.registerLanguage('typescript', require('highlight.js/lib/languages/typescript'))
hljs.registerLanguage('nginx', require('highlight.js/lib/languages/nginx'))
hljs.registerLanguage('dos', require('highlight.js/lib/languages/dos'))
hljs.registerLanguage('bash', require('highlight.js/lib/languages/bash'))
hljs.registerLanguage('shell', require('highlight.js/lib/languages/shell'))

class BuildNote {
  constructor () {
    // 删除并创建输出目录
    // fs.removeSync(outputDir)
    fs.ensureDirSync(outputDir)
    // 记录已经创建的模块
    this.hasCreated = {
      // 'js': true
    }

    // 查询索引数据
    this.searchIndexs = []
  }
  readMdCont (filePath, fileName, rootFileHandle) {
    let data = fs.readFileSync(filePath, 'utf8')
    if (rootFileHandle) data = rootFileHandle(data)
    // 去掉开头的 outline
    data = '\n' + data
      .replace(/^[\r\n]+/g, '')
      .replace(/^-\s\[[\s\S]*?\n#/, '#')
    // 目录下的文件名作为h1加上。根目录的文件不加
    if (!/^#\s/.test(data) && !rootFileHandle) {
      data = '# ' + this.getName(fileName) + '\n\n' + data
    }
    return data + '\n'
  }
  // writeFile (cont) {
  //   let filePath = path.resolve(outputDir, 'temp.txt')
  //   fs.writeFileSync(filePath, cont, 'utf8')
  // }
  writeFilePageJs ({ name, data }) {
    let filePath = path.resolve(outputDir, name + '.js')
    fs.writeFileSync(filePath, `window['cb_${name}'](${JSON.stringify(data)})`, 'utf8')

    // let filePath = path.resolve(outputDir, name + '.html')
    // fs.writeFileSync(filePath, data.content, 'utf8')
  }
  // 合并同一文件夹下的所有 md
  merge (dir, dirName) {
    let data = ''

    // 处理目录下的所有 md 文件，目前只处理 md
    try {
      const dirList = fs.readdirSync(dir)
      const mdReg = /\.md$/
      dirList.forEach(fileName => {
        // 目前只处理 md
        if (mdReg.test(fileName)) {
          data += this.readMdCont(path.resolve(dir, fileName), fileName)
        }
      })
    } catch {}

    // 处理对应的同名文件
    const fileName = dirName + '.md'
    try {
      const filePath = path.resolve(dir, '../' + fileName)
      data += this.readMdCont(filePath, fileName, data => data.replace(/(\n?)#(#+\s)/g, '$1$2'))
    } catch {}

    return data
  }
  build () {
    const dirList = fs.readdirSync(enterDir)
    const typeMenuList = []
    dirList.forEach(fileName => {
      const dirName = fileName.replace(/\.md$/, '')
      if (this.hasCreated[dirName]) return // 已创建将跳出
      const dir = path.resolve(enterDir, dirName)
      let mdCont = this.merge(dir, dirName)
      let name = this.getName(fileName)
      let mdData = this.getMdData({
        name,
        mdCont
      })
      this.writeFilePageJs({
        name,
        data: mdData
      })
      this.hasCreated[dirName] = true // 记录已经创建的模块
      typeMenuList.push({ name })
    })

    this.writeFilePageJs({
      name: 'typeMenuList',
      data: typeMenuList
    })

    /// 查询索引数据处理
    // 临时写文件测试
    // fs.writeFile(`${outputDir}/search-indexs.json`, JSON.stringify(this.searchIndexs), 'utf8')
    // 上传索引
    if (isBuildSearchIndexs) {
      const client = algoliasearch('AB8PXP326N', '414792795252da68e2e81916389d28c6')
      const index = client.initIndex('docs')
      index.clearIndex((err, content) => {
        if (err) throw err
        index.addObjects(this.searchIndexs, (err, content) => {
          if (err) throw err
        })
      })
    }
  }
  getName (fullName) {
    return fullName.replace(/^\d+_|\.md$/g, '')
  }
  GenerateOutline () {
    let startLevel = 0
    let rootItem = {
      children: []
    }
    let currItem = rootItem
    let currLevel = startLevel
    return {
      data: rootItem.children,
      add (text, level, id) {
        let breakLevel = level - currLevel
        // 兼容跳级
        if (breakLevel > 0) { // 跳子级
          for (let i = 0, len = breakLevel - 1; i < len; i++) {
            let item = {
              children: []
            }
            currItem.children.push(item)
            currItem = item
            currLevel = level + i
          }
        } else if (breakLevel <= 0) { // 跳父级
          currItem = rootItem
          for (let i = startLevel + 1; i < level; i++) {
            currItem = currItem.children[currItem.children.length - 1]
          }
        }

        let item = {
          id,
          level,
          name: text,
          children: []
        }
        currItem.children.push(item)
        currItem = item
        currLevel = level
      }
    }
  }
  generateId (cont) {
    // return uuid(cont, uuid.URL)
    let arr = []
    uuid(cont, uuid.URL, arr)
    return Buffer.from(arr).readUInt32LE().toString()
  }
  getMdData ({ name, mdCont }) {
    const renderer = new marked.Renderer()
    const generateOutline = this.GenerateOutline()

    let pre = 0
    let hxNum = 0
    let pathArr = [] // 标题层级
    let ids = []
    let index = 0
    renderer.heading = (text, depth) => {
      let hx = ''

      // 标题层级
      let pathIndex = pathArr.length = pathArr.length - 1 - pre + depth
      pathArr[pathIndex] = text

      // 根据层级生成id
      // console.log('--------', name + '/' + pathArr.join('/'))
      // let id = this.generateId(name + '/' + pathArr.join('/'))
      let id = ids[index++]

      generateOutline.add(text, depth, id)

      if (depth === pre) {
        hx = '</section>'
      } else if (depth < pre) {
        hx = (new Array(hxNum - depth + 2)).join('</section>')
        hxNum = depth - 1
      }
      hx = `${hx}<section><h${depth} id="${id}">${text}</h${depth}>`
      if (depth !== pre) {
        hxNum++
      }
      pre = depth
      return hx
    }

    marked.setOptions({
      renderer,
      highlight: code => hljs.highlightAuto(code).value
    })

    const tokens = marked.lexer(mdCont)
    this.buildSearchIndexs(tokens, name, ids)

    // 不压缩 html
    const htmlCont = marked.parser(tokens)

    // 压缩 html
    // const htmlCont = minify(marked.parser(tokens), {
    //   removeComments: true,
    //   collapseWhitespace: true,
    //   removeAttributeQuotes: true
    //   // more options:
    //   // https://github.com/kangax/html-minifier#options-quick-reference
    // })

    return {
      outline: generateOutline.data,
      content: htmlCont
    }
  }
  buildSearchIndexs (tokens, name, ids) {
    let indexs = this.searchIndexs
    let item = {
      name,
      outline: '',
      content: ''
    }
    let pathArr = []
    let preDepth = 0
    tokens.forEach(({ type, depth, text }) => {
      if (type === 'heading') {
        let length = pathArr.length = pathArr.length - 1 - preDepth + depth
        pathArr[length] = text
        let outline = name + '/' + pathArr.join('/')
        let id = this.generateId(outline)
        ids.push(id)
        item = {
          id,
          name,
          outline,
          content: ''
        }
        indexs.push(item)
        preDepth = depth
      } else if (text) {
        item.content += text.replace(/<(\w+)/g, '&lt;$1') + '\r\n'
      }
    })
  }
}

const buildNote = new BuildNote()
// buildNote.writeFile(
//   buildNote.readMdCont(
//     path.resolve(enterDir, 'nodejs', '文件系统 - fs.md'),
//     '文件系统 - fs.md'
//   )
// )
buildNote.build()
// console.log(dirList)
